<?php
/**
 * 標準キャッシュモジュール
 */
class vars {

	/**
	 * キャッシュ実態
	 */
	private $Cache;
	/**
	 * キャッシュ生存時間
	 */
	private $LifeTime;
	/**
	 * 現在時刻
	 */
	private $Now;

	/**
	 * 以後キャッシュを使用しないフラグ
	 */
	public $isNoCache;

	private $Act;
	private $Op;

	public function __construct () {
	// TODO: キャッシュ生存時間は別途設定できるようにする。
		$this->Cache = array();
		$this->LifeTime = 86400 * 7;
		$this->Now = time();
		
		$this->isNoCache = false;
	}
	
	public function __destruct () {
		if ($this->isNoCache) return;
		// キャッシュを保存する
		$this->saveSystemCache();
	}

	function init () {

		Y('this.removeDirectory+', YuTori::create($this, 'removeDirectory'));

		Y('this.sys+', null);
		Y('this.sys.cache+', YuTori::Create($this, 'systemCache'));
		Y('this.sys.cache.clear+', YuTori::Create($this, 'clearSystemCache'));

		Y('this.admin+', YuTori::create($this, 'admin'));
		Y('this.admin.checkDelCache+', YuTori::create($this, 'checkDelCache'));
		Y('this.admin.exedelcache+', YuTori::create($this, 'exedelcache'));
	}

	/**
	 * callはリクエストURIを直接確認する
	 */
	function call () {
		// Actの正当性チェック
		$this->Act = Y('Request.Global.Act');
		if (!preg_match('/mod|cache/', $this->Act)) return true;

		// Actディレクトリがない場合は作成する
		if (!file_exists (DIR_BASE_HTML.'/vars/'.$this->Act)) {
			@mkdir (DIR_BASE_HTML.'/vars/'.$this->Act);
		}
		
		// Actのメソッドに処理移行
		$Method = $this->Act;
		return $this->$Method ();
	}

	/**
	 * モジュール用ディレクトリ処理
	 */
	function mod () {
		$this->Op = Y('Request.Global.Op');
		$Path = explode ('/', $this->Op, 2);
		$FilePath = DIR_BASE_SYS. '/modules/extend/mod'.$Path[0].'/vars/';
		if (!file_exists($FilePath)) {
			$FilePath = DIR_BASE_SYS. '/modules/default/mod'.$Path[0].'/vars/';
		}
		if (file_exists($FilePath.$Path[1])) {
			$File = file_get_contents ($FilePath.$Path[1]);
			$Dir = dirname (DIR_BASE_HTML.'/vars/mod/'.$this->Op);
			if(!is_dir($Dir)) @mkdir ($Dir, 0777, true);
			@file_put_contents (DIR_BASE_HTML.'/vars/mod/'.$this->Op, $File);

			// テンプレート処理をカット
			Y('Document.TemplateOff');
			Y('Document.Name', $Path[1]);
			Y('Document.Direct', $File);
		}
		return true;
	}

	/**
	 * キャッシュ用ディレクトリ処理
	 */
	function cache () {
		// グローバルリクエスト２回シフト
		Y('Request.Global'); Y('Request.Global');
		// 対象モジュールをロード
		$Module = Y('Request.Global.Mod');
		Y('Module.load', $Module);
		
		// キャッシュコール
		return Y('Module.List.'.$Module.'.Cache');
	}

	/**
	 * ディレクトリを再帰的に削除する
	 */
	function removeDirectory($dir) {
		if (is_dir ($dir)) {
			foreach (scandir ($dir) as $item) {
				if ($item == "." || $item == "..") continue;
				if (is_dir("$dir/$item")) {
					$this->removeDirectory("$dir/$item");
				} else {
					@unlink("$dir/$item");
				}
			}
			@rmdir($dir);
		}
	}

	/**
	 * システムキャッシュの処理
	 * 引数の数によって処理が変わる。
	 * 引数１つの時：引数で指定したモジュールのキャッシュを読み込み、返す。
	 * 引数２つの時：第１引数で指定したモジュールのキャッシュとして、第２引数の値を代入する（名称なしキャッシュ）。
	 * 引数３つの時：第１引数で指定したモジュールのキャッシュとして、第２引数の値をキャッシュ名とし、第３引数の値を代入する。
	 */
	public function systemCache () {
		$Args = func_get_args();
		switch (count($Args)) {
			case 1:
				return $this->getSystemCache($Args[0]);
			case 2:
				return $this->setSystemCacheNoName($Args[0], $Args[1]);
			case 3:
				return $this->setSystemCache($Args[0], $Args[1], $Args[2]);
			default:
				return false;
		}
	}

	private function getSystemCache ($Mod) {
		Y('Logger.Debug', 'キャッシュ取得: '.$Mod);
		if (!isset ($this->Cache[$Mod])) {
			$Dir = DIR_BASE_SYS.'/vars/cache/'.$Mod;
			if(!is_dir($Dir)) @mkdir ($Dir, 0777, true);
			
			if (file_exists($Dir.'/cache.php')) {
				include ($Dir.'/cache.php');
			} else {
				$this->Cache[$Mod] = array();
			}
		}

		$Rt = array();
		foreach ($this->Cache[$Mod] as $Key=>$Val) {
			if ($Val['__LifeTime'] < $this->Now) {
				unset ($this->Cache[$Mod][$Key]);
			} else {
				$Rt[$Key] = $Val['__Value'];
			}
		}
		
		return $Rt;
	}

	private function setSystemCacheNoName ($Mod, $Value) {
		Y('Logger.Debug', '無名キャッシュのセット');
		
		$this->Cache[$Mod][] = array (
				'__LifeTime'=>$this->Now + $this->LifeTime,
				'__Value'=>$Value,
			);
		return true;
	}

	private function setSystemCache ($Mod, $Name, $Value) {
		Y('Logger.Debug', 'キャッシュのセット');
		
		$this->Cache[$Mod][$Name] = array (
				'__LifeTime'=>$this->Now + $this->LifeTime,
				'__Value'=>$Value,
			);
		return true;
	}

	public function clearSystemCache ($Mod, $Name) {
		Y('Logger.Debug', 'キャッシュクリア');
		if (!isset ($this->Cache[$Mod])) { $this->getSystemCache($Mod);}
		unset($this->Cache[$Mod][$Name]);
		return true;
	}

	private function saveSystemCache () {
		foreach ($this->Cache as $Mod=>$Cache) {
			$Dir = DIR_BASE_SYS.'/vars/cache/'.$Mod;
			if(!is_dir($Dir)) @mkdir ($Dir, 0777, true);

			// キャッシュの生存時間判定
			foreach ($Cache as $Key=>$Val) {
				if ($Val['__LifeTime'] < $this->Now) {
					unset ($Cache[$Key]);
				}
			}
			$File = var_export ($this->Cache[$Mod], true);
			$File = '<?php $this->Cache["'.$Mod.'"]='.$File.'; ?>';
			file_put_contents ($Dir.'/cache.php', $File);
		}

	}

	/**
	 * admin処理
	 */
	public function admin ($Method) {
		return Y('this.'.$Method);
	}
	
	public function checkDelCache () {
		Y('Document.load', 'Vars:admin/vars/checkdelcache', Y('Document.toObject$'));
		return true;
	}

	public function exedelcache () {
		Y('Logger.Debug', 'キャッシュ削除開始');
		$this->removeDirectory (DIR_BASE_HTML.'/vars/mod/');
		Y('Logger.Debug', DIR_BASE_HTML.'/vars/mod/を削除');
		$this->removeDirectory (DIR_BASE_HTML.'/vars/cache/');
		Y('Logger.Debug', DIR_BASE_HTML.'/vars/cache/を削除');
		$this->removeDirectory (DIR_BASE_SYS.'/vars/mod/');
		Y('Logger.Debug', DIR_BASE_SYS.'/vars/mod/を削除');
		$this->removeDirectory (DIR_BASE_SYS.'/vars/cache/');
		Y('Logger.Debug', DIR_BASE_SYS.'/vars/cache/を削除');
		Y('Vars.sys-');
		$this->isNoCache = true;
		$this->Cache = null;
		Y('Document.Object$')->DOG_appendChild('div')->addClass('information')->text('キャッシュを削除しました。');
		return true;
	}
}
?>